#include "../Base.h"

PAPC_ENTRY apc_entry = NULL;


VOID
defaultNormalRoutine(
	__in_opt PVOID NormalContext,
	__in_opt PVOID SystemArgument1,
	__in_opt PVOID SystemArgument2
) {
	LOG("Default APC Run \r\n");
}
pfnKeAlertThread keAlertThread;

VOID
BaseKernelRoutine(
	__in struct _KAPC* Apc,
	__deref_inout_opt PKNORMAL_ROUTINE* NormalRoutine,
	__deref_inout_opt PVOID* NormalContext,
	__deref_inout_opt PVOID* SystemArgument1,
	__deref_inout_opt PVOID* SystemArgument2
) 
{

	if (((PAPC_ENTRY)(*NormalContext))->func) {
		*NormalRoutine = (((PAPC_ENTRY)(*NormalContext))->func);
		KeSetEvent(&(((PAPC_ENTRY)(*NormalContext))->finishEvent), 0, FALSE);
	}
}


BOOLEAN InsertApc(ULONG tid, PKNORMAL_ROUTINE func) {

	PETHREAD eThread;
	NTSTATUS status = PsLookupThreadByThreadId((HANDLE)tid, &eThread);
	if (!NT_SUCCESS(status)) {
		return FALSE;
	}
	if (!apc_entry) {
		apc_entry =(PAPC_ENTRY) ExAllocatePoolWithTag(NonPagedPool, sizeof(APC_ENTRY), 'yang');
		memset(apc_entry, 0, sizeof(APC_ENTRY));
		apc_entry->func = func;
		KeInitializeApc(&apc_entry->apc,eThread,OriginalApcEnvironment,(PKKERNEL_ROUTINE)BaseKernelRoutine,NULL, defaultNormalRoutine,KernelMode, apc_entry);
		KeInitializeEvent(&apc_entry->finishEvent, SynchronizationEvent, FALSE);
	}
	return KeInsertQueueApc(&apc_entry->apc, NULL, NULL, 0);

}


VOID
normalRoutine(
	__in_opt PVOID NormalContext,
	__in_opt PVOID SystemArgument1,
	__in_opt PVOID SystemArgument2
) {
	PAPC_ENTRY apc_entry = (PAPC_ENTRY)NormalContext;
	KeSetEvent(&(apc_entry->finishEvent), 0, FALSE);
}



BOOLEAN InsertApcR3(ULONG tid, PKNORMAL_ROUTINE func) {

	PETHREAD eThread;
	ULONG funcAddress;
	
	char buf[] = { 0x8b,0x4d };
	NTSTATUS status = PsLookupThreadByThreadId((HANDLE)tid, &eThread);
	if (!NT_SUCCESS(status)) {
		return FALSE;
	}
	if (!apc_entry) {
		apc_entry = (PAPC_ENTRY)ExAllocatePoolWithTag(NonPagedPool, sizeof(APC_ENTRY), 'yang');
		memset(apc_entry, 0, sizeof(APC_ENTRY));
		apc_entry->func = func;
		KeInitializeApc(&apc_entry->apc, eThread, OriginalApcEnvironment, (PKKERNEL_ROUTINE)BaseKernelRoutine, NULL, defaultNormalRoutine, UserMode, apc_entry);
		KeInitializeEvent(&apc_entry->finishEvent, SynchronizationEvent, FALSE);
	}
	funcAddress = SsdtGetFunctionAddress("NtAlertThread");
	funcAddress = (ULONG)SearchPatterFuzzy((PCH)funcAddress, 0x100, buf, sizeof(buf));
	keAlertThread = (pfnKeAlertThread)(funcAddress + *(PULONG)(funcAddress - 4));
	eThread->Alertable |= 0x20;
	
	KeInsertQueueApc(&apc_entry->apc, NULL, NULL, 0);
	keAlertThread(eThread, UserMode);
	ObDereferenceObject(eThread);
	return TRUE;

}
 void APC_TEST() {
	 ULONG tid;
	 tid = 0x1e4;
	 PVOID  funAddress = (PVOID)0x401020;

	 if (InsertApc(tid,normalRoutine));
	 KeWaitForSingleObject(&apc_entry->finishEvent, Executive, KernelMode, FALSE, NULL);
	 ExFreePoolWithTag(apc_entry, 'yang');
	 apc_entry = NULL;
}

 void APC_test_R3() {

	 ULONG tid;
	 tid = 0x1e4;
	 PVOID  funAddress = (PVOID)0x401020;

	 if (InsertApcR3(tid, (PKNORMAL_ROUTINE)funAddress));
	 KeWaitForSingleObject(&apc_entry->finishEvent, Executive, KernelMode, FALSE, NULL);
	 ExFreePoolWithTag(apc_entry, 'yang');
	 apc_entry = NULL;

 }